package cn.tpshion.config.controller;

import cn.tpshion.config.common.DateUtil;
import cn.tpshion.config.common.FileUtil;
import cn.tpshion.config.common.JsonUtil;
import cn.tpshion.config.common.R;
import cn.tpshion.config.domain.dto.BuildCodeDTO;
import cn.tpshion.config.domain.dto.CodeConfigDTO;
import cn.tpshion.config.domain.dto.DeployDTO;
import cn.tpshion.config.domain.dto.DockerOpsDTO;
import cn.tpshion.config.domain.vo.BuildVO;
import cn.tpshion.config.domain.vo.CloneCodeVO;
import cn.tpshion.config.service.BuildService;
import cn.tpshion.config.websocket.WebSocketServer;
import com.github.dockerjava.api.DockerClient;
import com.github.dockerjava.api.command.CreateContainerResponse;
import com.github.dockerjava.api.model.*;
import com.github.dockerjava.core.DefaultDockerClientConfig;
import com.github.dockerjava.core.DockerClientBuilder;
import lombok.extern.slf4j.Slf4j;
import org.apache.maven.shared.invoker.InvocationResult;
import org.apache.maven.shared.utils.cli.CommandLineException;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.internal.storage.file.FileRepository;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.transport.UsernamePasswordCredentialsProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import java.io.File;
import java.io.IOException;
import java.util.List;

@RestController
@Slf4j
public class DepolyController {

    @Value("${target.dir}")
    private String targetDir;

    @Value("${target.mvn}")
    private String targetMvn;

    @Autowired
    private BuildService buildService;

    @Value("${docker.host}")
    private String dockerHost;

    @Autowired
    private WebSocketServer webSocketServer;

    @PostMapping("/getCode")
    public R<?> getCode(@RequestBody CodeConfigDTO dto){
        if(!StringUtils.hasText(dto.getPath())){
            return R.fail("代码地址不能为空");
        }
        if(!StringUtils.hasText(dto.getBranch())){
            return R.fail("代码分支不能为空");
        }

        if(!StringUtils.hasText(dto.getUsername()) || !StringUtils.hasText(dto.getPassword())){
            return R.fail("认证失败，用户名或密码错误！");
        }
        String uri = dto.getPath();
        String name = uri.substring(uri.lastIndexOf("/") + 1, uri.lastIndexOf("."));
        File file = new File(targetDir + name);

        try {
            Git git = null;
            if(file.exists() && file.isDirectory()){
                // 项目已存在，直接更新
                git = new Git(new FileRepository(file.getPath() + "/.git"));
                git.pull().setRemoteBranchName(dto.getBranch())
                        .setCredentialsProvider(new UsernamePasswordCredentialsProvider(dto.getUsername(), dto.getPassword()))
                        .call();
            }else{
                FileUtil.deleteFile(file);
                git = Git.cloneRepository()
                        .setURI(uri)
                        .setBranch(dto.getBranch())
                        .setDirectory(file)
                        .setCredentialsProvider(new UsernamePasswordCredentialsProvider(dto.getUsername(),dto.getPassword()))
                        .call();
            }
            Iterable<RevCommit> commits = git.log().call();
            String version = "";
            for (RevCommit revCommit : commits) {
                version = revCommit.getName().substring(0,8);
                break;
            }
            git.close();
            CloneCodeVO cloneCodeVO = new CloneCodeVO();
            cloneCodeVO.setBranch(dto.getBranch());
            cloneCodeVO.setName(name);
            cloneCodeVO.setVersion(version);
            cloneCodeVO.setStatus(1);
            cloneCodeVO.setMessage("拉取代码成功");
            log.info("拉取代码成功 cloneCodeVO:{}",cloneCodeVO);
            return R.success(cloneCodeVO,"拉取代码成功");
        } catch (Exception e) {
            log.error("git error:{}",e.getMessage(),e);
            CloneCodeVO cloneCodeVO = new CloneCodeVO();
            cloneCodeVO.setStatus(0);
            cloneCodeVO.setMessage("拉取代码失败");
            return R.success(cloneCodeVO);
        }
    }

    @PostMapping("/build")
    public R<?> buildCode(@RequestBody BuildCodeDTO dto){
        String pomPath = targetDir + dto.getName() + "/pom.xml";
        InvocationResult result = buildService.build(pomPath, targetMvn, dto.getId());
        BuildVO buildVO = new BuildVO();
        if(result.getExitCode() == 0){
            buildVO.setStatus(1);
            buildVO.setMessage("代码编译成功");
        }else{
            CommandLineException exception = result.getExecutionException();
            log.error("代码编译失败 id:{} errorMsg:{}", dto.getId(), exception.getMessage(), exception);
            buildVO.setStatus(0);
            buildVO.setMessage("代码编译失败");
        }
        return R.success(buildVO);
    }

    @PostMapping("/deploy")
    public R<?> deploy(@RequestBody DeployDTO dto){
        log.info("DeployDTO:{}", JsonUtil.obj2String(dto));
        BuildVO buildVO = new BuildVO();
        try {
            // 删除镜像
            DefaultDockerClientConfig.Builder builder = DefaultDockerClientConfig.createDefaultConfigBuilder();
            builder.withDockerHost(dockerHost);
            DockerClient client = DockerClientBuilder.getInstance(builder.build()).build();
            webSocketServer.sendMessage(dto.getId(), DateUtil.nowDate() + " docker连接成功");

            List<Image> imageList = client.listImagesCmd().exec();
            String imageName = "116.63.132.188:5000/"+ dto.getName() +":1.0.0";
            Image targetImage = null;
            for (Image image : imageList) {
                System.out.println(image);
                String[] repoTags = image.getRepoTags();
                for (String repoTag : repoTags) {
                    System.out.println(repoTag);
                    if(repoTag.equals(imageName)){
                        targetImage = image;
                        break;
                    }
                }
            }

            // 镜像存在需要删除镜像
            if(targetImage != null){
                // 有运行的容器先停止容器
                List<Container> containerList = client.listContainersCmd().withShowAll(true).exec();
                Container targetContainer = null;
                for (Container container : containerList) {
                    log.info("image:{} containerId:{}",container.getImage(), container.getId());
                    webSocketServer.sendMessage(dto.getId(),
                            DateUtil.nowDate() + " image:" + container.getImage() + " containerId:" + container.getId());
                    if(container.getImage().equals(imageName)){
                        targetContainer = container;
                        break;
                    }
                }

                if(targetContainer != null){
                    List<Container> runContainers = client.listContainersCmd().exec();
                    boolean targetRunFlag = false;
                    for (Container runContainer : runContainers) {
                        if(runContainer.getId().equals(targetContainer.getId())){
                            targetRunFlag = true;
                            break;
                        }
                    }
                    if(targetRunFlag){
                        log.info("开始停止运行容器:{} id:{}", targetContainer.getImage(), targetContainer.getId());
                        webSocketServer.sendMessage(dto.getId(),
                                DateUtil.nowDate() + " 开始停止运行容器:"+ targetContainer.getImage() + " id:" + targetContainer.getId());
                        client.stopContainerCmd(targetContainer.getId()).exec();
                        webSocketServer.sendMessage(dto.getId(), DateUtil.nowDate() + " " + targetContainer.getImage() + " 容器停止成功!");
                    }
                    // 删除容器
                    client.removeContainerCmd(targetContainer.getId()).exec();
                    webSocketServer.sendMessage(dto.getId(), DateUtil.nowDate()+ " " + targetContainer.getImage() + " 容器删除成功!");
                }
                // 删除镜像
                client.removeImageCmd(targetImage.getId()).exec();
                webSocketServer.sendMessage(dto.getId(),DateUtil.nowDate() + "删除镜像：" + imageName +"成功！");
            }
            client.close();

            String pomPath = targetDir + dto.getName() + "/pom.xml";
            InvocationResult result = buildService.deploy(pomPath, targetMvn, dto.getId(), dto.getSkipTest());
            if(result.getExitCode() == 0){
                buildVO.setStatus(1);
                buildVO.setMessage("代码打包成功");
                return R.success(buildVO);
            }else{
                CommandLineException exception = result.getExecutionException();
                log.error("代码打包失败 id:{} errorMsg:{}", dto.getId(), exception.getMessage(), exception);
                buildVO.setStatus(0);
                buildVO.setMessage("代码打包失败");
                return R.success(buildVO);
            }
        }catch (Exception e){
            log.error("deploy error id:{} errorMsg:{}", dto.getId(), e.getMessage(),e);
            buildVO.setStatus(0);
            buildVO.setMessage("代码打包失败！");
            return R.success(buildVO);
        }
    }

    @PostMapping("/docker")
    public R<?> dockerOps(@RequestBody DockerOpsDTO dto) {
        log.info("DockerOpsDTO:{}",JsonUtil.obj2String(dto));
        BuildVO buildVO = new BuildVO();
        try {
            webSocketServer.sendMessage(dto.getId(), DateUtil.nowDate() + " 开始连接docker host:" + dockerHost);
            DefaultDockerClientConfig.Builder builder = DefaultDockerClientConfig.createDefaultConfigBuilder();
            builder.withDockerHost(dockerHost);
            DockerClient client = DockerClientBuilder.getInstance(builder.build()).build();
            Info info = client.infoCmd().exec();
            webSocketServer.sendMessage(dto.getId(), DateUtil.nowDate() + " docker连接成功");
            webSocketServer.sendMessage(dto.getId(), DateUtil.nowDate() + JsonUtil.obj2StringPretty(info));
            // 获取镜像信息
            List<Image> imageList = client.listImagesCmd().exec();
            boolean targetImage = false;
            for (Image image : imageList) {
                String[] repoTags = image.getRepoTags();
                for (String repoTag : repoTags) {
                    log.info("镜像：{}",repoTag);
                    webSocketServer.sendMessage(dto.getId(), DateUtil.nowDate() + " 镜像：" + repoTag);
                    if(repoTag.equals(dto.getImagesName())){
                        targetImage = true;
                    }
                }
            }
            if(!targetImage){
                log.info("未找到已构建镜像：{}",dto.getImagesName());
                buildVO.setStatus(0);
                buildVO.setMessage("部署失败，打包镜像不存在");
                return R.success(buildVO);
            }

            List<Container> containerList = client.listContainersCmd().withShowAll(true).exec();
            Container targetContainer = null;
            for (Container container : containerList) {
                log.info("image:{} containerId:{}",container.getImage(), container.getId());
                webSocketServer.sendMessage(dto.getId(),
                        DateUtil.nowDate() + " image:" + container.getImage() + " containerId:" + container.getId());
                if(container.getImage().equals(dto.getImagesName())){
                    targetContainer = container;
                    break;
                }
            }

            if(targetContainer != null){
                List<Container> runContainers = client.listContainersCmd().exec();
                boolean targetRunFlag = false;
                for (Container runContainer : runContainers) {
                    if(runContainer.getId().equals(targetContainer.getId())){
                        targetRunFlag = true;
                        break;
                    }
                }
                if(targetRunFlag){
                    log.info("开始停止运行容器:{} id:{}", targetContainer.getImage(), targetContainer.getId());
                    webSocketServer.sendMessage(dto.getId(),
                            DateUtil.nowDate() + " 开始停止运行容器:"+ targetContainer.getImage() + " id:" + targetContainer.getId());
                    client.stopContainerCmd(targetContainer.getId()).exec();
                    webSocketServer.sendMessage(dto.getId(), DateUtil.nowDate() + " " + targetContainer.getImage() + " 容器停止成功!");
                }
                // 删除容器
                client.removeContainerCmd(targetContainer.getId()).exec();
                webSocketServer.sendMessage(dto.getId(), DateUtil.nowDate()+ " " + targetContainer.getImage() + " 容器删除成功!");
            }

            webSocketServer.sendMessage(dto.getId(),DateUtil.nowDate() + " 开始创建容器：" + dto.getImagesName());
            CreateContainerResponse containerResponse = createContainers(client, dto.getName(), dto.getImagesName(), dto.getPort(), dto.getPort());

            webSocketServer.sendMessage(dto.getId(),DateUtil.nowDate() + " " + dto.getImagesName() + " 容器创建成功 id:" + containerResponse.getId());

            // 启动容器
            client.startContainerCmd(containerResponse.getId()).exec();
            webSocketServer.sendMessage(dto.getId(),DateUtil.nowDate() + " " + dto.getImagesName() + " 容器启动成功 id:" + containerResponse.getId());

            client.close();
            buildVO.setStatus(1);
            buildVO.setMessage("部署成功");
            return R.success(buildVO);
        }catch (Exception e){
            log.error("deploy error id:{} errorMsg:{}", dto.getId(), e.getMessage(),e);
            buildVO.setStatus(0);
            buildVO.setMessage("代码部署失败！");
            return R.success(buildVO);
        }
    }

    private CreateContainerResponse createContainers(DockerClient client, String containerName, String imageName, String internalPort, String externalPort ){
        //映射端口 externalPort(物理机外部端口) -> internalPort(容器内部端口)
        ExposedPort exposedPort = ExposedPort.tcp(Integer.valueOf(internalPort));
        Ports portBindings = new Ports();
        portBindings.bind(exposedPort, Ports.Binding.bindPort(Integer.valueOf(externalPort)));

        CreateContainerResponse container = client.createContainerCmd(imageName)
                .withName(containerName)
                .withHostConfig(new HostConfig().withPortBindings(portBindings))
                .withExposedPorts(exposedPort).exec();
        return container;
    }
}
